{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "db340a22",
   "metadata": {},
   "source": [
    "# Return Electron Output Values from Lattices\n",
    "\n",
    "To avoid causing graph construction issues and being scolded by the Covalent server post-processor, ensure that the value returned by a lattice is the output of an electron.\n",
    "\n",
    "Many best practices with Covalent boil down to \"use the decorators.\" Putting as much of the working code as possible inside Covalent's decorators (electrons and lattices) enables the Covalent server to manage execution as intended.\n",
    " \n",
    "## Context\n",
    "\n",
    "Covalent allows you to put business logic in a lattice outside of an electron, but that doesn't mean you should do so. Violating this practice puts results outside of Covalent's ability to run code on executors. Following this practice ensures that your lattices, at least in this respect, will work with future versions of Covalent.\n",
    "\n",
    "## Best Practice\n",
    "\n",
    "Keep computations inside electrons. Use lattices to execute sequences of electrons, not to perform computations.\n",
    "\n",
    "## Example\n",
    "\n",
    "Contrast the two examples below.\n",
    "\n",
    "### Example 1: Not Recommended\n",
    "\n",
    "This example demonstrates the incorrect approach. Notice all the computation that occurs in the lattice: a list of random samples is created, then returned as a numpy array. However, the list cannot be created when the lattice is run since `res`, the second parameter to the `random.sample()` method, is an unexecuted electron. As a result, the `random.sample()` method fails."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e419b89d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import covalent as ct\n",
    "import numpy as np\n",
    "import random\n",
    "\n",
    "# Technique 1:\n",
    "\n",
    "@ct.electron\n",
    "def task_1(x):\n",
    "    return x * 2\n",
    "\n",
    "@ct.lattice\n",
    "def workflow(a):\n",
    "    res = task_1(a)\n",
    "    res_list = random.sample(range(10, 30), res) # this will fail at graph construction time since `res` is still an Electron\n",
    "    return np.array(res_list)\n",
    "\n",
    "# Uncomment the two following statements to demonstrate \n",
    "# id = ct.dispatch(workflow)(1)\n",
    "# result = ct.get_result(id, wait=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "06f0c7fc",
   "metadata": {},
   "source": [
    "### Example 2: Improved\n",
    "\n",
    "In contrast, the  following code properly contains the construction of `res_list` in an electron, `task_2_new`.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "9ce44a45",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Lattice Result\n",
      "==============\n",
      "status: COMPLETED\n",
      "result: [19 11]\n",
      "input args: ['1']\n",
      "input kwargs: {}\n",
      "error: None\n",
      "\n",
      "start_time: 2023-03-13 21:27:15.248600\n",
      "end_time: 2023-03-13 21:27:15.404917\n",
      "\n",
      "results_dir: /Users/dave/.local/share/covalent/data\n",
      "dispatch_id: 642292a8-758c-42cd-a2d3-757755388207\n",
      "\n",
      "Node Outputs\n",
      "------------\n",
      "task_1(0): 2\n",
      ":parameter:1(1): 1\n",
      "task_2_new(2): [19 11]\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import covalent as ct\n",
    "import numpy as np\n",
    "import random\n",
    "\n",
    "# Technique 2:\n",
    "\n",
    "@ct.electron\n",
    "def task_1(x):\n",
    "    return x * 2\n",
    "\n",
    "@ct.electron\n",
    "def task_2_new(x):\n",
    "    res_list = random.sample(range(10, 30), x)\n",
    "    return np.array(res_list)\n",
    "\n",
    "@ct.lattice\n",
    "def workflow_2(a):\n",
    "    res_1 = task_1(a)\n",
    "    return task_2_new(res_1)\n",
    "\n",
    "id = ct.dispatch(workflow_2)(1)\n",
    "result = ct.get_result(id, wait=True)\n",
    "\n",
    "print(result)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
